package org.cybzacg.encryption.strategy.impl;

import org.cybzacg.encryption.enums.EncryptionStatus;
import org.cybzacg.encryption.strategy.SecurityStrategy;
import org.cybzacg.encryption.context.EncryptionContext;
import org.cybzacg.encryption.model.EncryptionResult;
import org.cybzacg.encryption.model.ValidationResult;
import org.cybzacg.encryption.utils.ValidationUtils;
import org.cybzacg.encryption.enums.SecurityStrategyEnum;

import javax.crypto.SecretKey;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 时间窗口安全策略
 * 基于时间窗口的访问频率限制和异常检测
 *
 * @author encryption team
 * @version 1.0
 */
public class TimeWindowSecurityStrategy implements SecurityStrategy {

    private final long windowSizeMillis;
    private final int maxAttemptsPerWindow;
    private final long blockDurationMillis;
    private final double anomalyThreshold;

    // 访问统计
    private final Map<String, AccessStats> accessStatsMap = new ConcurrentHashMap<>();

    // 全局统计
    private final AtomicLong totalRequests = new AtomicLong(0);
    private final AtomicLong blockedRequests = new AtomicLong(0);
    private final AtomicInteger activeBlocks = new AtomicInteger(0);

    /**
     * 访问统计信息
     */
    private static class AccessStats {
        private final AtomicLong requestCount = new AtomicLong(0);
        private final AtomicLong blockedCount = new AtomicLong(0);
        private volatile long windowStartTime = System.currentTimeMillis();
        private volatile long blockEndTime = 0;
        private volatile boolean isBlocked = false;

        public void resetWindow() {
            requestCount.set(0);
            windowStartTime = System.currentTimeMillis();
        }

        public boolean isWindowExpired(TimeWindowSecurityStrategy strategy) {
            return System.currentTimeMillis() - windowStartTime > strategy.windowSizeMillis;
        }

        public void incrementRequest() {
            requestCount.incrementAndGet();
        }

        public void incrementBlocked() {
            blockedCount.incrementAndGet();
        }

        public long getRequestCount() {
            return requestCount.get();
        }

        public long getBlockedCount() {
            return blockedCount.get();
        }

        public boolean isBlocked() {
            return isBlocked && System.currentTimeMillis() < blockEndTime;
        }

        public void block(long duration) {
            isBlocked = true;
            blockEndTime = System.currentTimeMillis() + duration;
        }

        public void unblock() {
            isBlocked = false;
            blockEndTime = 0;
        }

        public long getWindowStartTime() {
            return windowStartTime;
        }
    }

    /**
     * 构造函数
     *
     * @param windowSizeSeconds 时间窗口大小（秒）
     * @param maxAttemptsPerWindow 每个窗口最大尝试次数
     * @param blockDurationSeconds 阻塞持续时间（秒）
     * @param anomalyThreshold 异常检测阈值
     */
    public TimeWindowSecurityStrategy(int windowSizeSeconds, int maxAttemptsPerWindow,
                                      int blockDurationSeconds, double anomalyThreshold) {
        this.windowSizeMillis = windowSizeSeconds * 1000L;
        this.maxAttemptsPerWindow = maxAttemptsPerWindow;
        this.blockDurationMillis = blockDurationSeconds * 1000L;
        this.anomalyThreshold = anomalyThreshold;
    }

    /**
     * 默认构造函数
     */
    public TimeWindowSecurityStrategy() {
        this(60, 10, 300, 0.1); // 1分钟窗口，最多10次，阻塞5分钟，10%异常阈值
    }

    @Override
    public ValidationResult validateBeforeEncryption(byte[] data, EncryptionContext context) {
        String clientId = getClientId(context);
        AccessStats stats = accessStatsMap.computeIfAbsent(clientId, k -> new AccessStats());

        totalRequests.incrementAndGet();

        // 检查是否被阻塞
        if (stats.isBlocked()) {
            blockedRequests.incrementAndGet();
            return new ValidationResult.Builder()
                    .valid(false)
                    .message("Request blocked due to rate limit violation")
                    .error("Client " + clientId + " is blocked until " +
                        LocalDateTime.now().plusSeconds(stats.blockEndTime / 1000))
                    .build();
        }

        // 检查时间窗口
        if (stats.isWindowExpired(this)) {
            stats.resetWindow();
        }

        // 增加请求计数
        stats.incrementRequest();

        // 检查是否超过限制
        if (stats.getRequestCount() >= maxAttemptsPerWindow) {
            stats.block(blockDurationMillis);
            activeBlocks.incrementAndGet();
            blockedRequests.incrementAndGet();

            return new ValidationResult.Builder()
                    .valid(false)
                    .message("Rate limit exceeded")
                    .error(String.format("Client %s exceeded maximum requests (%d) per window (%d seconds). " +
                        "Blocked for %d seconds.",
                        clientId, maxAttemptsPerWindow, windowSizeMillis / 1000, blockDurationMillis / 1000))
                    .build();
        }

        // 检查数据大小
        int maxDataSize = getMaxDataSize(context.getAlgorithm());
        if (data.length > maxDataSize) {
            return new ValidationResult.Builder()
                    .valid(false)
                    .message("Data size exceeds maximum allowed")
                    .error(String.format("Data size %d bytes exceeds maximum %d bytes for algorithm %s",
                        data.length, maxDataSize, context.getAlgorithm()))
                    .build();
        }

        return new ValidationResult.Builder()
                .valid(true)
                .message("Request allowed by time window security strategy")
                .build();
    }

    @Override
    public ValidationResult validateBeforeDecryption(byte[] encryptedData, EncryptionContext context) {
        // 检查加密数据大小
        int maxDataSize = getMaxDataSize(context.getAlgorithm());
        if (encryptedData.length > maxDataSize) {
            return new ValidationResult.Builder()
                    .valid(false)
                    .message("Encrypted data size exceeds maximum allowed")
                    .error(String.format("Encrypted data size %d bytes exceeds maximum %d bytes for algorithm %s",
                        encryptedData.length, maxDataSize, context.getAlgorithm()))
                    .build();
        }

        return new ValidationResult.Builder()
                .valid(true)
                .message("Decryption request allowed")
                .build();
    }

    @Override
    public EncryptionResult processEncryptionResult(EncryptionResult result, EncryptionContext context) {
        // 检查结果的有效性
        if (result == null) {
            return EncryptionResult.failure(
                    EncryptionStatus.FAILURE,
                "Encryption result is null",
                context
            );
        }

        if (!result.isSuccess()) {
            return EncryptionResult.failure(
                EncryptionStatus.FAILURE,
                "Encryption operation failed: " + result.getErrorMessage(),
                context
            );
        }

        // 检查数据完整性
        if (result.getData() == null || result.getData().length == 0) {
            return EncryptionResult.failure(
                EncryptionStatus.FAILURE,
                "Encrypted data is empty or null",
                context
            );
        }

        // 添加时间戳信息
        return new EncryptionResult.Builder()
                .algorithm(result.getAlgorithm())
                .data(result.getData())
                .metadata("processedAt", System.currentTimeMillis())
                .metadata("strategy", "TimeWindowSecurityStrategy")
                .context(context)
                .build();
    }

    @Override
    public EncryptionResult processDecryptionResult(EncryptionResult result, EncryptionContext context) {
        // 类似于加密结果处理
        if (result == null) {
            return EncryptionResult.failure(
                EncryptionStatus.FAILURE,
                "Decryption result is null",
                context
            );
        }

        if (!result.isSuccess()) {
            return EncryptionResult.failure(
                EncryptionStatus.FAILURE,
                "Decryption operation failed: " + result.getErrorMessage(),
                context
            );
        }

        return new EncryptionResult.Builder()
                .algorithm(result.getAlgorithm())
                .data(result.getData())
                .metadata("processedAt", System.currentTimeMillis())
                .metadata("strategy", "TimeWindowSecurityStrategy")
                .context(context)
                .build();
    }

    @Override
    public Exception handleException(Exception exception, EncryptionContext context) {
        // 记录异常统计
        String clientId = getClientId(context);
        AccessStats stats = accessStatsMap.get(clientId);
        if (stats != null) {
            stats.incrementBlocked();
        }

        // 根据异常类型决定处理策略
        if (exception instanceof SecurityException) {
            return exception; // 安全异常直接抛出
        } else if (exception instanceof RuntimeException) {
            return new SecurityException("Security violation detected", exception);
        } else {
            return new SecurityException("Security error during processing", exception);
        }
    }

    @Override
    public SecurityStrategyEnum getStrategyType() {
        return SecurityStrategyEnum.TIME_WINDOW;
    }

    @Override
    public String getDescription() {
        return "Time Window Security Strategy - Rate limiting and anomaly detection based on sliding time windows";
    }

    @Override
    public boolean supports(EncryptionContext context) {
        return context != null && context.getAlgorithm() != null;
    }

    @Override
    public int getPriority() {
        return 2; // 中等优先级
    }

    /**
     * 异常检测
     */
    private ValidationResult detectAnomalies(String clientId, AccessStats stats) {
        // 检测异常高频访问
        double requestRate = (double) stats.getRequestCount() /
                (System.currentTimeMillis() - stats.getWindowStartTime()) * 1000;

        if (requestRate > anomalyThreshold * 1000) { // 转换为每秒请求数
            return new ValidationResult.Builder()
                    .valid(true)
                    .warning("Anomalous access pattern detected")
                    .error(String.format("Client %s shows unusually high request rate: %.2f requests/sec",
                        clientId, requestRate))
                    .build();
        }

        // 检测阻塞模式
        if (stats.getBlockedCount() > 0) {
            double blockRate = (double) stats.getBlockedCount() / stats.getRequestCount();
            if (blockRate > 0.5) { // 超过50%的请求被阻塞
                return new ValidationResult.Builder()
                        .valid(true)
                        .warning("High block rate detected")
                        .error(String.format("Client %s has high block rate: %.2f%%", clientId, blockRate * 100))
                        .build();
            }
        }

        return new ValidationResult.Builder()
                .valid(true)
                .message("No anomalies detected")
                .build();
    }

    /**
     * 获取客户端ID
     */
    private String getClientId(EncryptionContext context) {
        return "unknown";
    }

    /**
     * 获取最大数据大小
     */
    private int getMaxDataSize(org.cybzacg.encryption.enums.EncryptionAlgorithm algorithm) {
        return switch (algorithm) {
            case AES -> 1024 * 1024; // 1MB
            case RSA -> 245; // RSA-2048的最大数据大小
            case DES -> 8; // DES块大小
            case TRIPLE_DES -> 8; // 3DES块大小
            case BLOWFISH -> 56; // Blowfish块大小
            default -> 1024; // 默认1KB
        };
    }

    /**
     * 获取安全统计信息
     */
    public SecurityStatistics getStatistics() {
        Map<String, ClientStatistics> clientStats = new HashMap<>();

        for (Map.Entry<String, AccessStats> entry : accessStatsMap.entrySet()) {
            AccessStats stats = entry.getValue();
            clientStats.put(entry.getKey(), new ClientStatistics(
                stats.getRequestCount(),
                stats.getBlockedCount(),
                stats.isBlocked(),
                stats.getWindowStartTime()
            ));
        }

        return new SecurityStatistics(
            totalRequests.get(),
            blockedRequests.get(),
            activeBlocks.get(),
            clientStats,
                 (windowSizeMillis / 1000),
            maxAttemptsPerWindow,
                (blockDurationMillis / 1000)
        );
    }

    /**
     * 重置指定客户端的统计信息
     */
    public void resetClientStats(String clientId) {
        accessStatsMap.remove(clientId);
    }

    /**
     * 重置所有统计信息
     */
    public void resetAllStats() {
        accessStatsMap.clear();
        totalRequests.set(0);
        blockedRequests.set(0);
        activeBlocks.set(0);
    }

    /**
     * 手动阻塞客户端
     */
    public void blockClient(String clientId, long durationSeconds) {
        AccessStats stats = accessStatsMap.get(clientId);
        if (stats != null) {
            stats.block(durationSeconds * 1000);
            activeBlocks.incrementAndGet();
        }
    }

    /**
     * 手动解除客户端阻塞
     */
    public void unblockClient(String clientId) {
        AccessStats stats = accessStatsMap.get(clientId);
        if (stats != null && stats.isBlocked()) {
            stats.unblock();
            activeBlocks.decrementAndGet();
        }
    }

    /**
     * 检查客户端是否被阻塞
     */
    public boolean isClientBlocked(String clientId) {
        AccessStats stats = accessStatsMap.get(clientId);
        return stats != null && stats.isBlocked();
    }

    /**
     * 获取客户端的剩余阻塞时间
     */
    public long getRemainingBlockTime(String clientId) {
        AccessStats stats = accessStatsMap.get(clientId);
        if (stats != null && stats.isBlocked()) {
            return Math.max(0, stats.blockEndTime - System.currentTimeMillis());
        }
        return 0;
    }

    /**
     * 安全统计信息类
     */
    public static class SecurityStatistics {
        private final long totalRequests;
        private final long blockedRequests;
        private final int activeBlocks;
        private final Map<String, ClientStatistics> clientStats;
        private final long windowSizeSeconds;
        private final int maxAttemptsPerWindow;
        private final long blockDurationSeconds;

        public SecurityStatistics(long totalRequests, long blockedRequests, int activeBlocks,
                               Map<String, ClientStatistics> clientStats, long windowSizeSeconds,
                               int maxAttemptsPerWindow, long blockDurationSeconds) {
            this.totalRequests = totalRequests;
            this.blockedRequests = blockedRequests;
            this.activeBlocks = activeBlocks;
            this.clientStats = clientStats;
            this.windowSizeSeconds = windowSizeSeconds;
            this.maxAttemptsPerWindow = maxAttemptsPerWindow;
            this.blockDurationSeconds = blockDurationSeconds;
        }

        public long getTotalRequests() {
            return totalRequests;
        }

        public long getBlockedRequests() {
            return blockedRequests;
        }

        public int getActiveBlocks() {
            return activeBlocks;
        }

        public Map<String, ClientStatistics> getClientStats() {
            return clientStats;
        }

        public long getWindowSizeSeconds() {
            return windowSizeSeconds;
        }

        public int getMaxAttemptsPerWindow() {
            return maxAttemptsPerWindow;
        }

        public long getBlockDurationSeconds() {
            return blockDurationSeconds;
        }

        public double getBlockRate() {
            return totalRequests > 0 ? (double) blockedRequests / totalRequests : 0.0;
        }

        @Override
        public String toString() {
            return String.format(
                "SecurityStatistics:\n" +
                "  Total Requests: %d\n" +
                "  Blocked Requests: %d (%.2f%%)\n" +
                "  Active Blocks: %d\n" +
                "  Window Size: %d seconds\n" +
                "  Max Attempts: %d\n" +
                "  Block Duration: %d seconds\n" +
                "  Tracked Clients: %d",
                totalRequests, blockedRequests, getBlockRate() * 100, activeBlocks,
                windowSizeSeconds, maxAttemptsPerWindow, blockDurationSeconds,
                clientStats.size()
            );
        }
    }

    /**
     * 客户端统计信息类
     */
    public static class ClientStatistics {
        private final long requestCount;
        private final long blockedCount;
        private final boolean isBlocked;
        private final long windowStartTime;

        public ClientStatistics(long requestCount, long blockedCount, boolean isBlocked, long windowStartTime) {
            this.requestCount = requestCount;
            this.blockedCount = blockedCount;
            this.isBlocked = isBlocked;
            this.windowStartTime = windowStartTime;
        }

        public long getRequestCount() {
            return requestCount;
        }

        public long getBlockedCount() {
            return blockedCount;
        }

        public boolean isBlocked() {
            return isBlocked;
        }

        public long getWindowStartTime() {
            return windowStartTime;
        }

        public double getBlockRate() {
            return requestCount > 0 ? (double) blockedCount / requestCount : 0.0;
        }

        @Override
        public String toString() {
            return String.format(
                "ClientStatistics[requests=%d, blocked=%d (%.2f%%), blocked=%s]",
                requestCount, blockedCount, getBlockRate() * 100, isBlocked
            );
        }
    }
}
